Javascript 宣告變數方式是
var name = "flash";
但是如果你不小心這樣寫
var name = "flash";
var name = "abc";
在任何程式碼編輯器編輯這段程式碼的時候, 不會提示你重複宣告. Javascript 執行時期(runtime) 也不會爆出任何錯誤.
Typescript 提供了let 關鍵字, 你可以動手試試看在VSCode 中, 將上述程式碼改成
let name = "flash";
let name = "abc";
VSCode 會提示你錯誤
Cannot redeclare block-scoped variable 'name'.
還有一種情況, 在Javascript 中這樣寫
for (var i = 0; i < 10 ; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
想要測試Javascript 執行結果, 你可以打開任何瀏覽器, 打開開發人員工具控制台, 將Javascript 程式碼內容複製貼上, 並執行看看.
猜猜看上面的Javascript 程式碼會輸出什麼樣的結果? 大多數人都會直覺的回答
0 1 2 3 4 5 6 7 8 9
但是實際上跑出來的結果卻是
10 10 10 10 10 10 10 10 10 10
因為var 有個獲取(Capturing) 問題, 而且 setTimeout 會在 for 迴圈之後才執行, 而 for 迴圈跑完之後i 變數就變成 10, 便輸出的同一個變數 i (10)
Typescript 可以解決這樣的問題, 用 let 就解決了
for (let i = 0; i < 10 ; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
還有一種Javascript 怪異的現象, 假設你輸出不存在的變數
console.log(a);
打開瀏覽器的開發人員工具控制台, 貼上上段程式碼, 你應該看到
Uncaught ReferenceError: a is not defined
這結果很合理, 但是你改成下面程式碼, 再一次貼到開發人員工具控制台
console.log(a);
var a = 10;
結果卻是
undefined
我剛入門Javascript 的時候, 看到這就傻眼了....
JavaScript 是一種直譯解釋性程式語言.
剛接觸到Javascript 的人都有這一句介紹話: "JavaScript 是一種直譯解釋性程式語言"
但是實際上, 這句話只對一半, Javascript 執行的時候透過V8 (Google 開發的Javascript 引擎)編譯成中間機器碼, 然後由瀏覽器逐行加載解釋執行.
所以呢, 我貼上去的Javascript 原始碼在瀏覽器並不是 "直接" 直譯執行, 而是先被轉換為中間機器碼, 再直譯中間機器碼一行一行逐行加載解釋執行.
但就單論程式碼來看, 至少應該給我輸出 "Uncaught ReferenceError: a is not defined" 這才合理阿,
那為什麼Javascript 要改變這種不合理的行為?
從小學習C/C++ 的人都知道, 我們一定要先宣告變數才可以使用, 但在Javascript 世界裡可以不用宣告變數就使用. 例如以下
i = 123;
console.log(i);
從小學習C/C++ 的人也知道, 我們也一定要宣告函式才可以使用(就是 .h .hpp 定義檔), 但在Javascript 世界裡, Javascript 也希望我們不用宣告任何函式就可以使用.
甚麼!? 看看下面的例子
test();
function test() {
console.log("hello world");
}
你會說, 這很簡單, 把 function test 宣告移到上面, 不就好了嗎? 這很合情合理, 宣告函式之後就可以使用, 這很合理阿! 為什麼要 "不用宣告" 這東西?
function test() {
console.log("hello world");
}
test();
再舉個下面的例子
function walk(n) {
if( n>10 ) {
logEvent(n);
}
}
function logEvent(msg) {
console.log(msg);
walk(1);
}
假如不想要類似 .h .hpp 宣告檔案, 又要有 "一定要宣告函式" 這件事情, 那麼直譯walk() 裡面程式碼的時候會發現到 logEvent() 因為沒有在前面定義宣告, 會跑出直譯錯誤.
但假如我們把 logEvent() 在原始碼檔案中順序位置移到 walk() 上面, 但直譯logEvent() 的時候又會發現walk() 沒有在前面定義宣告, 又再次跑出直譯錯誤.
你會發現這很像遞迴問題....
故Javascript 存在著 var 提升(hoisting) 的問題
而且如果使用var 的時候不小心這樣寫的話
x = 5;
console.log(x);
... 中間隔了程式碼
var x;
你的Javascript 就會出現不可預期的錯誤.
為了說明Typescript 他的好, 首先我們先從let 方式開始吧! Typescript 可以讓 let 不允許你重複宣告同樣的變數名稱, 也不允許你未經定義宣告就使用變數.
Typescript 也提供了 const 關鍵字
const name = "flash";
const 的功能和 let 一樣是宣告變數, 但不同的地方是 const 如同其名是不允許變數內容被改變的.
Typescript 宣告變數基本類型有
let name: string = "";
let id: number = 123;
let birth: Date = new Date();
let myArr: number[] = [];
let myBool: boolean = false;
Typescript 沒有直接提供字典(Dictionary) 關鍵字讓我們直接使用字典, 不過我們可以限制其約束.
例如下面是宣告字典的範例
class Student {
name: string;
id: number;
}
let dict: {[id: number]: Student} = {};
宣告字典的地方是以 大括號開頭和結束 包起來的, 裡面寫
[id: number]
表示這是字典的Key, Key 的名稱是id , Key 的型態是 number.
使用字典的時候, 可以這樣使用
let student = new Student();
dict[student.id] = student;
假如你誤用以下程式碼
dict[student.id] = 123;
Typescript 就會提示你錯誤
Type '123' is not assignable to type 'Student'.
let 跟 const 應該是 JavaScript(ES6) 本身就有提供的東西,而不是 typescript 所提供的喔。就算沒有使用 typescript 也可以用 let 跟 const
想當年我是在2012年接觸Typescript, 也就是六年前沒有ES6 ...
雖然說 TypeScript 是 2012 年出現的沒錯,但 let 跟 const 是在 2015 年的 1 月的 TypeScript 1.4 版本才支援的喔。裡面也有提到 let 與 const 是 ES6 的東西。
相關連結: